package compiler

import (
	"os"
	"fmt"
	"embed"
	"errors"
	"io/fs"
	"io/ioutil"
	"path/filepath"
)


type FileSystem interface {
	Open(path string) (File, error)
	Key() string
}
type File interface {
	Close() error
	Info() (FileInfo, error)
	ReadDir() ([] FileInfo, error)
	ReadContent() ([] byte, error)
}
type FileInfo = fs.FileInfo
type FileMode = fs.FileMode

func ReadFile(path string, fs FileSystem) ([] byte, error) {
	var f, err = fs.Open(path)
	if err != nil { return nil, err }
	{ var content, err = f.ReadContent()
		if err != nil { return nil, err }
		{ var err = f.Close()
			if err != nil { return nil, err }
			return content, nil } }
}
func ReadFolder(path string, fs FileSystem) (FileInfo, [] FileInfo, error) {
	var f, err = fs.Open(path)
	if err != nil { return nil, nil, err }
	{ var info, err = f.Info()
		if err != nil { return nil, nil, err }
		{ var items, err = f.ReadDir()
			if err != nil { return nil, nil, err }
			{ var err = f.Close()
				if err != nil { return nil, nil, err } }
			return info, items, nil } }
}


type RealFileSystem struct {}
type RealFile struct {
	Fd  *os.File
}
func (_ RealFileSystem) Open(path string) (File, error) {
	var fd, err = os.Open(path)
	if err != nil { return nil, err }
	return RealFile { fd }, nil
}
func (_ RealFileSystem) Key() string {
	return ""
}
func (f RealFile) Close() error {
	return f.Fd.Close()
}
func (f RealFile) Info() (FileInfo, error) {
	return f.Fd.Stat()
}
func (f RealFile) ReadDir() ([] FileInfo, error) {
	return f.Fd.Readdir(0)
}
func (f RealFile) ReadContent() ([] byte, error) {
	return ioutil.ReadAll(f.Fd)
}

type EmbeddedFileSystem struct {
	Id  string
	FS  embed.FS
}
type StdFile struct {
	File  fs.File
}
func (s EmbeddedFileSystem) Open(path string) (File, error) {
	var f, err = s.FS.Open(filepath.ToSlash(path))
	if err != nil { return nil, err }
	return StdFile { f }, nil
}
func (s EmbeddedFileSystem) Key() string {
	return fmt.Sprintf("(embedded.%s)", s.Id)
}
func (f StdFile) Close() error {
	return f.File.Close()
}
func (f StdFile) Info() (FileInfo, error) {
	return f.File.Stat()
}
func (f StdFile) ReadDir() ([] FileInfo, error) {
	var dir, is_dir = f.File.(fs.ReadDirFile)
	if !(is_dir) { return nil, errors.New("not a directory") }
	var entry_list, err = dir.ReadDir(-1)
	var info_list = make([] FileInfo, len(entry_list))
	for i, entry := range entry_list {
		var info, err = entry.Info()
		if err != nil { return nil, err }
		info_list[i] = info
	}
	return info_list, err
}
func (f StdFile) ReadContent() ([]byte, error) {
	return ioutil.ReadAll(f.File)
}


